Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Thread Programming

Hay que saber cómo levantar un thread en los lenguajes que vayamos a usar,
para entender cómo los tratan por abajo.

Java

private static void hello() {
    var t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello from thread 1");
        }
    });
    var t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello from thread 2");
        }
    });
    t1.start();
    t2.start();
}

// Con Lambdas
private static void helloLambda() {
    var t1 = new Thread(() -> System.out.println("Hello from thread 1"));
    var t2 = new Thread(() -> System.out.println("Hello from thread 1"));
    t1.start();
    t2.start();
}

Cabe destacar que lo anterior simplemente levanta los threads, que se ejecutarán como el procesador los quiera ejecutar.

Administrar manualmente la ejecución

private static void hello() throws InterruptedException {
    var t1 = new Thread(() -> System.out.println("Hello from thread 1"));
    var t2 = new Thread(() -> System.out.println("Hello from thread 2"));
    t1.start();
    t2.start();
    t1.join();
    t2.join();
}

El método join espera a que el thread termine su ejecución.
Si no se llama a join, el thread principal puede terminar antes que los threads secundarios.

Denle importancia al throws InterruptedException. La firma de esta función es así porque, al querer unir los threads al principal (o esperar a que terminen), puede haber una interrupción.

Al llamar a este método, se habilita a que cualquier thread interrumpa el actual (donde se está ejecutando esta función) por la razón que sea.

Rust

#![allow(unused)]
fn main() {
fn hello() {
    thread::spawn(|| println!("Hello from thread 1"));
    thread::spawn(|| println!("Hello from thread 2"));
}
}

Esperar a que termine la ejecución

#![allow(unused)]
fn main() {
fn hello() {
    let t1 = thread::spawn(|| println!("Hello from thread 1"));
    let t2 = thread::spawn(|| println!("Hello from thread 2"));

    // Esperar a que los threads se completen
    t1.join().expect("t1 failed");
    t2.join().expect("t2 failed");
}
}

Thread Scope

El Scope es una forma de agrupar threads, de forma tal que se ejecuten en un mismo contexto.
Además, el Scope permite que los threads se compartan variables entre ellos, sin necesidad de usar Arc o Mutex (para casos de solo lectura).

Una vez termina el Scope, los threads se unen automáticamente, por lo que no es necesario llamar a join manualmente.

#![allow(unused)]
fn main() {
fn hello() {
    thread::scope(|s| {
        s.spawn(|| println!("Hello from thread 1"));
        s.spawn(|| println!("Hello from thread 2"));
    });
}
}

Lifetime

Sabiendo que Rust sabe que los threads no escapan del scope:

  • Las reglas de Lifetime son más simples
  • Podemos usar variables del scope externo
#![allow(unused)]
fn main() {
fn hello() {
    let n = 10;
    let mut m = 10;
    thread::scope(|s| {
        s.spawn(|| {
            m += 1;
            println!("Hello from thread 1, n = {n}")
        });
        s.spawn(|| println!("Hello from thread 2, n = {n}"));
    });
    println!("{m}");
}
// Output: 11
}